﻿//-----------------------------------------------------------------------
// <copyright file="TupleIndexer.cs" company="tuliprules.org">
//     Copyright (c) 2010 tuplelister, tuliprules.org.
// </copyright>
// <summary>This is the TupleIndexer class.</summary>
//-----------------------------------------------------------------------

namespace Tulip.Engine.Tuples
{
    using System;
    using System.Collections;
    using System.Collections.Generic;

#pragma warning disable 1587
    /// <summary>
    /// TupleIndexer.   Contains an evaluator (delegate) used to maintian Tuple views.
    /// An indexer can contain additional nested indexers to represent compound indexes.
    /// </summary>
    /// <typeparam name="TTuple">The tuple type.</typeparam>
#if MONO201_COMPLIANT
    public class TupleIndexer<TTuple> : IEnumerable<TupleIndexer<TTuple>>
#else
    public class TupleIndexer<TTuple> : IEnumerable<TupleIndexer<TTuple>>, IDisposable where TTuple : Tuple
#endif
#pragma warning restore 1587
    {
        /// <summary>
        /// Indexer identifier.   Used by Tuples to retrieve the correct 'next'
        /// and 'previous' references within linked lists of tuples.
        /// </summary>
        public int Id;

        /// <summary>
        /// Flag indicates if a tuple list should be stored for this indexer.
        /// </summary>
        ///// <remarks>
        ///// In a composite index, it may only be necessary to store tuples for the
        ///// index as a whole (i.e., against the last indexer in the chain), or it
        ///// may be neccessary to store tuple lists against indexers higher in the 
        ///// chain in situations where a compound index subsumes simpler indexes.
        ///// </remarks>
        public bool StoreTupleList = true;

        /// <summary>
        /// The tuple indexer evalation record stored by this indexer.
        /// </summary>
        private TupleIndexerEvaluationRecord<TTuple> tupleIndexerEvalRec;

        /// <summary>
        /// The next indexer for a compound index.
        /// </summary>
        private TupleIndexer<TTuple> nextIndexer;

        /// <summary>
        /// The current enumerator.
        /// </summary>
        private TupleIndexerEnumerator currentEnumerator;

        /// <summary>
        /// Flag indicates if the enumerator has been disposed.
        /// </summary>
        private bool disposed;

        /// <summary>
        /// Initializes a new instance of the TupleIndexer class.
        /// </summary>
        /// <param name="id">Unique identifier for the indexer.</param>
        /// <param name="tupleIndexerEvaluationRecord">The tuple indexer evaluator used to index the tuple.</param>
        public TupleIndexer(int id, TupleIndexerEvaluationRecord<TTuple> tupleIndexerEvaluationRecord)
        {
            this.Id = id;
#if PORT_TO_CPPCLI
            // C++/CLI has issues with a constructor that recurses indirectly via
            // a called method.  This leads to malformed IL that does not carry enough
            // generic parameter data to allow TupleIndexer to be instantiated.   Hence, we
            // in-line the AddTupleIndexerEvaluationRecord code here for porting purposes.

            if (this.tupleIndexerEvaluationRecord.EvaluateTuple == null)
            {
                    this.tupleIndexerEvaluationRecord = tupleIndexerEvaluationRecord;
            }
            else
            {
                var currentIndexer = this;
                var nextIndexer = this.nextIndexer;

                int nestingCount = 1;

                while (!(nextIndexer == null))
                {
                    currentIndexer = nextIndexer;
                    nextIndexer = nextIndexer.nextIndexer;
                    nestingCount++;
                }

                currentIndexer.nextIndexer = new TupleIndexer<TTuple>(this.Id + nestingCount, tupleIndexerEvaluationRecord);
            }

            // Invalidate any current enumerator
            if (!(this.currentEnumerator == null))
            {
                this.currentEnumerator.CollectionChangeNotification();
            }
#else
            this.AddTupleIndexerEvaluationRecord(tupleIndexerEvaluationRecord);
#endif
        }

        /// <summary>
        /// Initializes a new instance of the  class.
        /// Constructs the non-'all' indexers.
        /// </summary>
        /// <param name="id">Unique identifier for the indexer.</param>
        /// <param name="tupleIndexerEvaluationRecords">The tuple indexer evaluators used to index the tuple.</param>
        public TupleIndexer(int id, params TupleIndexerEvaluationRecord<TTuple>[] tupleIndexerEvaluationRecords)
        {
            this.Id = id;

            foreach (var tupleIndexerEvaluationRecord in tupleIndexerEvaluationRecords)
            {
                this.AddTupleIndexerEvaluationRecord(tupleIndexerEvaluationRecord);
            }
        }

        /// <summary>
        /// Initializes a new instance of the TupleIndexer class.
        /// Constructs the 'all' indexers.
        /// </summary>
        /// <param name="id">Unique identifier for the indexer.</param>
        public TupleIndexer(int id)
        {
            this.Id = id;
            this.CreateAllTupleIndexer();
        }

        /// <summary>
        /// Finalizes an instance of the TupleIndexer class.
        /// </summary>
        ~TupleIndexer()
        {
            this.Dispose(false);
        }

        /// <summary>
        /// Gets or sets the evaluator stored by this indexer.
        /// </summary>
        public Evaluate<TTuple> Evaluator
        {
            get
            {
                return this.tupleIndexerEvalRec.EvaluateTuple;
            }

            set
            {
                this.tupleIndexerEvalRec.EvaluateTuple = value;
            }
        }

        /// <summary>
        /// Gets or sets the tuple view factory stored by this indexer.
        /// </summary>
        public TupleViewFactory TupleViewFactory
        {
            get
            {
                return this.tupleIndexerEvalRec.CreateTupleView;
            }

            set
            {
                this.tupleIndexerEvalRec.CreateTupleView = value;
            }
        }

        /// <summary>
        /// Dispose the tuple indexer.
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Adds an evaluator to the tail of the nested list of indexers.
        /// </summary>
        /// <param name="tupleIndexerEvaluationRecord">Struct record that relates a tuple indexer and a tuple view delegate.</param>
        public void AddTupleIndexerEvaluationRecord(TupleIndexerEvaluationRecord<TTuple> tupleIndexerEvaluationRecord)
        {
            if (this.tupleIndexerEvalRec.EvaluateTuple == null)
            {
                this.tupleIndexerEvalRec = tupleIndexerEvaluationRecord;
                System.Diagnostics.Debug.Assert(
                    this.nextIndexer == null,
                    "The next tuple indexer should currently be null");
            }
            else
            {
                var currentIndexer = this;
                var nextIdx = this.nextIndexer;

                var nestingCount = 1;

                while (nextIdx != null)
                {
                    currentIndexer = nextIdx;
                    nextIdx = nextIdx.nextIndexer;
                    nestingCount++;
                }

                currentIndexer.nextIndexer = new TupleIndexer<TTuple>(
                    this.Id + nestingCount,
                    tupleIndexerEvaluationRecord);
            }

            // Invalidate any current enumerator
            if (this.currentEnumerator != null)
            {
                this.currentEnumerator.CollectionChangeNotification();
            }
        }

        /// <summary>
        /// Returns a new enumerator for tuple indexer evaluators.
        /// </summary>
        /// <returns>An enumerator for the tuple indexer evaluators.</returns>
        public IEnumerator<TupleIndexer<TTuple>> GetEnumerator()
        {
            // The only indexer we will ever see externally is the head indexer.
            this.DisposeCurrentEnumerator();
            this.currentEnumerator = new TupleIndexerEnumerator(this);
            return this.currentEnumerator;
        }

        /// <summary>
        /// Returns a new enumerator for Tuple Indexer evaluators.
        /// </summary>
        /// <returns>An enumerator for Tuple Indexer evaluators.</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        /// <summary>
        /// Dummy method used to initialise the evaluator used within a TupleIndexerEvalRec as key for 
        /// retrieving all tuples. This method is never called.
        /// </summary>
        /// <param name="tuple">Dummy tuple.</param>
        /// <returns>Allways returns null.</returns>
        private static IComparable EvaluateAll(TTuple tuple)
        {
            return null;
        }

        /// <summary>
        /// Dummy method used to initialise a TupleIndexerEvalRec with a tuple view factory delegate.
        /// This method is never called.
        /// </summary>
        /// <returns>A new tuple view dictionary.</returns>
        private static IDictionary GetTupleViewAll()
        {
            return new Dictionary<Type, TupleLinkedList>();
        }

        /// <summary>
        /// Initialises 'all' indexer.
        /// </summary>
        private void CreateAllTupleIndexer()
        {
            this.AddTupleIndexerEvaluationRecord(new TupleIndexerEvaluationRecord<TTuple>(EvaluateAll, GetTupleViewAll));
        }

        /// <summary>
        /// Disposes the current enumerator.
        /// </summary>
        private void DisposeCurrentEnumerator()
        {
            this.currentEnumerator = null;
        }

        /// <summary>
        /// Disposes of resources for the tuple indexer.
        /// </summary>
        /// <param name="disposing">Indicates if the Dispose was called explicitly.</param>
        private void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    this.currentEnumerator.Dispose();
                }
            }

            this.disposed = true;
        }

        /////////////// <summary>
        /////////////// Adds an evaluator to the tail of the nested list of indexers.
        /////////////// </summary>
        /////////////// <param name="evaluator">An evaluator used to index the tuple.</param>
        ////////////public void AddEvaluator(Evaluate<TTuple> evaluator)
        ////////////{
        ////////////    System.Diagnostics.Debug.Assert(evaluator != null, "The evaluator cannot be null");

        ////////////    if (_evaluator == null)
        ////////////    {
        ////////////        _evaluator = evaluator;
        ////////////        System.Diagnostics.Debug.Assert(_nextIndexer == null, "The next tuple indexer should currently be null");
        ////////////    }
        ////////////    else
        ////////////    {
        ////////////        var currentIndexer = this;
        ////////////        var nextIndexer = _nextIndexer;

        ////////////        var nestingCount = 1;

        ////////////        while (nextIndexer != null)
        ////////////        {
        ////////////            currentIndexer = nextIndexer;
        ////////////            nextIndexer = nextIndexer._nextIndexer;
        ////////////            nestingCount++;
        ////////////        }

        ////////////        currentIndexer._nextIndexer = new TupleIndexer<TTuple>(_id + nestingCount, evaluator);
        ////////////    }

        ////////////    // Invalidate any current enumerator
        ////////////    if (_currentEnumerator != null)
        ////////////    {
        ////////////        _currentEnumerator.CollectionChangeNotification();
        ////////////    }
        ////////////}

        ///////////// <summary>
        ///////////// Adds an evaluator to the tail of the nested list of indexors.
        ///////////// </summary>
        ///////////// <param name="evaluator">Evaluators used to index the tuple.</param>
        //////////public void AddEvaluators(params Evaluate<TTuple>[] evaluators)
        //////////{
        //////////    foreach (var evaluator in evaluators)
        //////////    {
        //////////        AddEvaluator(evaluator);
        //////////    }
        //////////}

        /// <summary>
        /// Enumerator for Tuple Indexers.   Enumerates the evalutaor delegates
        /// stored in each Tuple Indexer in nested order.
        /// </summary>
        public class TupleIndexerEnumerator : IEnumerator<TupleIndexer<TTuple>>
        {
            /// <summary>
            /// Head tuple indexer.
            /// </summary>
            private TupleIndexer<TTuple> headIndexer;

            /// <summary>
            /// The current indexer.
            /// </summary>
            private TupleIndexer<TTuple> currentIndexer;

            /// <summary>
            /// Flag indicates if the enumerator is invalid.
            /// </summary>
            private bool isInvalidated;

            /// <summary>
            /// Flag indicates if the enumerator has been disposed.
            /// </summary>
            private bool disposed;

            /// <summary>
            /// Flag indicates if a first attempt at calling Current has been made after
            /// the enumerator has been invalidated.   On the first attempt, Current returns 
            /// the current Evaluator if the enumerator was invalidated after the previous#
            /// call to MoveNext.
            /// </summary>
            private bool currentFirstAttemptDoneOnInvalid;

            /// <summary>
            /// Initializes a new instance of the TupleIndexerEnumerator class.
            /// </summary>
            /// <param name="headIndexer">Head tuple indexer.</param>
            public TupleIndexerEnumerator(TupleIndexer<TTuple> headIndexer)
            {
                System.Diagnostics.Debug.Assert(headIndexer != null, "The head indexer cannot be null.");
                this.headIndexer = headIndexer;
            }

            /// <summary>
            /// Finalizes an instance of the TupleIndexerEnumerator class.
            /// </summary>
            ~TupleIndexerEnumerator()
            {
                this.Dispose(false);
            }

            /// <summary>
            /// Gets the evaluator for the current tuple indexer.
            /// </summary>
            public TupleIndexer<TTuple> Current
            {
                get
                {
                    if (this.isInvalidated)
                    {
                        if (this.currentFirstAttemptDoneOnInvalid)
                        {
                            throw new InvalidOperationException(Properties.Resources.ExTuplesTupleIndexerEnumeratorInvalidated);
                        }

                        this.currentFirstAttemptDoneOnInvalid = true;
                        return this.currentIndexer;
                    }

                    return this.currentIndexer;
                }
            }

            /// <summary>
            /// Gets the current tuple indexer element.
            /// </summary>
            object IEnumerator.Current
            {
                get
                {
                    return this.Current;
                }
            }

            /// <summary>
            /// Dispose the enumerator.
            /// </summary>
            public void Dispose()
            {
                this.Dispose(true);
                GC.SuppressFinalize(this);
            }

            /// <summary>
            /// Moves to the next tuple indexer in nested order.
            /// </summary>
            /// <returns>Boolean value indicating if the enumerator succesfully moved to the next tuple indexer.</returns>
            public bool MoveNext()
            {
                if (this.isInvalidated)
                {
                    this.currentFirstAttemptDoneOnInvalid = true;  // Simulate calling Current.   Current should  always fail in this case.
                    throw new InvalidOperationException(Properties.Resources.ExTuplesTupleIndexerEnumeratorInvalidated);
                }

                if (this.currentIndexer == null)
                {
                    this.currentIndexer = this.headIndexer;
                    return true;
                }

                if (this.currentIndexer.nextIndexer == null)
                {
                    return false;
                }

                this.currentIndexer = this.currentIndexer.nextIndexer;
                return true;
            }

            /// <summary>
            /// Resets the enumerator to before the first tuple indexer element.
            /// </summary>
            public void Reset()
            {
                if (this.isInvalidated)
                {
                    throw new InvalidOperationException(Properties.Resources.ExTuplesTupleIndexerEnumeratorInvalidated);
                }

                this.currentIndexer = null;
            }

            /// <summary>
            /// Notifies the enumerator of a change to the collection.
            /// </summary>
            internal void CollectionChangeNotification()
            {
                this.isInvalidated = true;
            }

            /// <summary>
            /// Disposes of resources for the tuple indexer enumerator.
            /// </summary>
            /// <param name="disposing">Indicates if the Dispose was called explicitly.</param>
            private void Dispose(bool disposing)
            {
                if (!this.disposed)
                {
                    if (disposing)
                    {
                        this.headIndexer.DisposeCurrentEnumerator();
                        this.headIndexer = null;
                        this.currentIndexer = null;
                    }
                }

                this.disposed = true;
            }
        }
    }
}
